;(function (global) {

    if ("EventSource" in global) return;
    
    var reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
    
    var EventSource = function (url) {
      var eventsource = this,  
          interval = 500, // polling interval  
          lastEventId = null,
          cache = '';
    
      if (!url || typeof url != 'string') {
        throw new SyntaxError('Not enough arguments');
      }
    
      this.URL = url;
      this.readyState = this.CONNECTING;
      this._pollTimer = null;
      this._xhr = null;
      
      function pollAgain(interval) {
        eventsource._pollTimer = setTimeout(function () {
          poll.call(eventsource);
        }, interval);
      }
      
      function poll() {
        try { // force hiding of the error message... insane?
          if (eventsource.readyState == eventsource.CLOSED) return;
    
          // NOTE: IE7 and upwards support
          var xhr = new XMLHttpRequest();
          xhr.open('GET', eventsource.URL, true);
          xhr.setRequestHeader('Accept', 'text/event-stream');
          xhr.setRequestHeader('Cache-Control', 'no-cache');
          // we must make use of this on the server side if we're working with Android - because they don't trigger 
          // readychange until the server connection is closed
          xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    
          if (lastEventId != null) xhr.setRequestHeader('Last-Event-ID', lastEventId);
          cache = '';
        
          xhr.timeout = 50000;
          xhr.onreadystatechange = function () {
            if (this.readyState == 3 || (this.readyState == 4 && this.status == 200)) {
              // on success
              if (eventsource.readyState == eventsource.CONNECTING) {
                eventsource.readyState = eventsource.OPEN;
                eventsource.dispatchEvent('open', { type: 'open' });
              }
    
              var responseText = '';
              try {
                responseText = this.responseText || '';
              } catch (e) {}
            
              // process this.responseText
              var parts = responseText.substr(cache.length).split("\n"),
                  eventType = 'message',
                  data = [],
                  i = 0,
                  line = '';
                
              cache = responseText;
            
              // TODO handle 'event' (for buffer name), retry
              for (; i < parts.length; i++) {
                line = parts[i].replace(reTrim, '');
                if (line.indexOf('event') == 0) {
                  eventType = line.replace(/event:?\s*/, '');
                } else if (line.indexOf('retry') == 0) {                           
                  var retry = parseInt(line.replace(/retry:?\s*/, ''));
                  if(!isNaN(retry)) { interval = retry; }
                } else if (line.indexOf('data') == 0) {
                  data.push(line.replace(/data:?\s*/, ''));
                } else if (line.indexOf('id:') == 0) {
                  lastEventId = line.replace(/id:?\s*/, '');
                } else if (line.indexOf('id') == 0) { // this resets the id
                  lastEventId = null;
                } else if (line == '') {
                  if (data.length) {
                    var event = new MessageEvent(data.join('\n'), eventsource.url, lastEventId);
                    eventsource.dispatchEvent(eventType, event);
                    data = [];
                    eventType = 'message';
                  }
                }
              }
    
              if (this.readyState == 4) pollAgain(interval);
              // don't need to poll again, because we're long-loading
            } else if (eventsource.readyState !== eventsource.CLOSED) {
              if (this.readyState == 4) { // and some other status
                // dispatch error
                eventsource.readyState = eventsource.CONNECTING;
                eventsource.dispatchEvent('error', { type: 'error' });
                pollAgain(interval);
              } else if (this.readyState == 0) { // likely aborted
                pollAgain(interval);
              } else {
              }
            }
          };
        
          xhr.send();
        
          setTimeout(function () {
            if (true || xhr.readyState == 3) xhr.abort();
          }, xhr.timeout);
          
          eventsource._xhr = xhr;
        
        } catch (e) { // in an attempt to silence the errors
          eventsource.dispatchEvent('error', { type: 'error', data: e.message }); // ???
        } 
      };
      
      poll(); // init now
    };
    
    EventSource.prototype = {
      close: function () {
        // closes the connection - disabling the polling
        this.readyState = this.CLOSED;
        clearInterval(this._pollTimer);
        this._xhr.abort();
      },
      CONNECTING: 0,
      OPEN: 1,
      CLOSED: 2,
      dispatchEvent: function (type, event) {
        var handlers = this['_' + type + 'Handlers'];
        if (handlers) {
          for (var i = 0; i < handlers.length; i++) {
            handlers[i].call(this, event);
          }
        }
    
        if (this['on' + type]) {
          this['on' + type].call(this, event);
        }
      },
      addEventListener: function (type, handler) {
        if (!this['_' + type + 'Handlers']) {
          this['_' + type + 'Handlers'] = [];
        }
        
        this['_' + type + 'Handlers'].push(handler);
      },
      removeEventListener: function (type, handler) {
        var handlers = this['_' + type + 'Handlers'];
        if (!handlers) {
          return;
        }
        for (var i = handlers.length - 1; i >= 0; --i) {
          if (handlers[i] === handler) {
            handlers.splice(i, 1);
            break;
          }
        }
      },
      onerror: null,
      onmessage: null,
      onopen: null,
      readyState: 0,
      URL: ''
    };
    
    var MessageEvent = function (data, origin, lastEventId) {
      this.data = data;
      this.origin = origin;
      this.lastEventId = lastEventId || '';
    };
    
    MessageEvent.prototype = {
      data: null,
      type: 'message',
      lastEventId: '',
      origin: ''
    };
    
    if ('module' in global) module.exports = EventSource;
    global.EventSource = EventSource;
     
    })(this);